package wechatpay

import (
	"bytes"
	"context"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"github.com/gogap/logs"
	"image"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
	"yundian/internel/app/helper/request"
	"yundian/internel/pkg/authorized"
	"yundian/internel/app/models/paysetting"
)

// ------------ Api v3 - payApi(支付API) -----------------//

var clientPool *sync.Pool
var zmClientPool *sync.Pool

func init() {
	clientPool = &sync.Pool{
		New: func() interface{} {
			return NewTlsClient()
		},
	}
	zmClientPool = &sync.Pool{
		New: func() interface{} {
			return NewZmTlsClient()
		},
	}
}

// 创建微信支付分订单 文档： https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_1.shtml
func ApiPayScoreOrder(apiData *PayData, wc *paysetting.PaySetting) (resultData *PayData, err error) {

	authorization, err := GenerateToken("POST", wxServiceOrder, apiData.ToJson(), wc)
	if err != nil {
		return nil, err
	}
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url := StrJoin(wxBaseUrl, wxServiceOrder)

	respData, err := client.Post(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, apiData.ToJson())
	log.Println("打印测试数据----创建微信支付分订单------ ApiPayScoreOrder-respData", respData)

	return respData, err
}

// 获取商户当前可用的平台证书列表 https://api.mch.weixin.qq.com/v3/certificates
// 注 - 微信支付平台证书 不是商户平台证书
func ApiCertificates(wc *paysetting.PaySetting) (respData *PayData, err error) {

	authorization, err := GenerateToken("GET", wxCertificates, "", wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url := StrJoin(wxBaseUrl, wxCertificates)
	respData, err = client.Get(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"User-Agent":    userAgent,
		"Authorization": authorization,
	})
	log.Println("打印测试数据----获取当前可用的平台证书列表---- ApiCertificates-respData", respData)

	return
}

// 查询微信支付分订单 文档：https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_2.shtml
func ApiQueryPayScoreOrder(outOrderNo string, wc *paysetting.PaySetting) (respData *PayData, err error) {
	param := fmt.Sprintf("service_id=%s&out_order_no=%s&appid=%s", wc.ServerId,
		outOrderNo, wc.AppId)
	url := StrJoin(wxServiceOrder, "?", param)
	authorization, err := GenerateToken("GET", url, "", wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	logs.Info("打印测试 ------- 配置", wc.TypeName)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url = StrJoin(wxBaseUrl, url)
	log.Println("打印测试数据 ApiQueryPayScoreOrder-url", url)

	respData, err = client.Get(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"User-Agent":    userAgent,
		"Authorization": authorization,
	})
	log.Println("打印测试数据----查询微信支付分订单----- ApiQueryPayScoreOrder-respData", respData)
	return
}

// 完结支付分订单   文档：https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_5.shtml
// v3/payscore/serviceorder/{out_order_no}/complete
func ApiCompletePayScoreOrder(apiData string, tradeNo string, wc *paysetting.PaySetting) (respData *PayData, err error) {

	url := StrJoin(wxServiceOrder, "/", tradeNo, "/complete")

	authorization, err := GenerateToken("POST", url, apiData, wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url = StrJoin(wxBaseUrl, url)
	//log.Println("url-----", url)
	respData, err = client.Post(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, apiData)
	log.Println("打印测试数据----完结支付分订单------ ApiCompletePayScoreOrder-respData", respData)

	return
}

// 同步支付分订单   文档：https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_7.shtml
// v3/payscore/serviceorder/{out_order_no}/sync
func ApiSyncPayScoreOrder(apiData *PayData, tradeNo string, wc *paysetting.PaySetting) (respData *PayData, err error) {

	url := StrJoin(wxServiceOrder, "/", tradeNo, "/complete")
	authorization, err := GenerateToken("POST", url, apiData.ToJson(), wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url = StrJoin(wxBaseUrl, url)
	respData, err = client.Post(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, apiData.ToJson())
	log.Println("打印测试数据---同步支付分订单--- ApiSyncPayScoreOrder-respData", respData)

	return
}

// 取消支付分订单   文档: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_3.shtml
// v3/payscore/serviceorder/{out_order_no}/cancel
func ApiCancelPayScoreOrder(apiData *PayData, outOrderNo string, wc *paysetting.PaySetting) (respData *PayData, err error) {

	url := StrJoin(wxServiceOrder, "/", outOrderNo, "/cancel")
	authorization, err := GenerateToken("POST", url, apiData.ToJson(), wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url = StrJoin(wxBaseUrl, url)
	respData, err = client.Post(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, apiData.ToJson())
	log.Println("打印测试数据----取消支付分订单--- ApiCancelPayScoreOrder-respData", respData)

	return
}

// jsapi下订单   文档: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml
// v3/pay/transactions/jsapi
func ApiJsApiPay(apiData *PayData, wc *paysetting.PaySetting) (respData *PayData, err error) {

	authorization, err := GenerateToken("POST", wxTransactions, apiData.ToJson(), wc)
	if err != nil {
		return nil, err
	}

	var client *PayClient
	if wc.TypeName == "zm" { // 中曼速充
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else { // 国曼科技
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url := StrJoin(wxBaseUrl, wxTransactions)

	respData, err = client.Post(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, apiData.ToJson())
	logs.Info("打印测试数据---统一下订单--- apijsapipay-respData", respData.ToJson())

	return
}

// 申请退款   文档: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_26.shtml
// v3/refund/domestic/refunds
func ApiRefundsPayScore(apiData *PayData, wc *paysetting.PaySetting) (respData *PayData, err error) {

	authorization, err := GenerateToken("POST", wxRefunds, apiData.ToJson(), wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)

	var client *PayClient
	if wc.TypeName == "zm" { // 中曼速充
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else { // 国曼科技
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url := StrJoin(wxBaseUrl, wxRefunds)
	//log.Println("打印测试数据---申请退款--- ApiRefundsPayScore-url", url)

	respData, err = client.Post(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, apiData.ToJson())
	//log.Println("打印测试数据---申请退款--- ApiRefundsPayScore-respData", respData)

	return
}

// 查询微信支付订单 https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}?mchid=1230000109
func ApiQueryTransactionsOrder(outOrderNo string, wc *paysetting.PaySetting) (respData *PayData, err error) {
	url := StrJoin(wxQueryPayOrders, "/out-trade-no/", outOrderNo, "?mchid=", wc.MchId)

	authorization, err := GenerateToken("GET", url, "", wc)
	if err != nil {
		return nil, err
	}

	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url = StrJoin(wxBaseUrl, url)
	log.Println("打印测试数据 ApiQueryPayScoreOrder-url", url)

	respData, err = client.Get(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"User-Agent":    userAgent,
		"Authorization": authorization,
	})
	log.Println("打印测试数据----查询微信支付订单----- ApiQueryPayScoreOrder-respData", respData)
	return
}

//查询退款
func GetRefundsInfo(outRefundNo string, wc *paysetting.PaySetting) (respData *PayData, err error) {
	url := StrJoin(wxRefunds, "/", outRefundNo)
	authorization, err := GenerateToken("GET", url, "", wc)
	if err != nil {
		return nil, err
	}
	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}
	url = StrJoin(wxBaseUrl, url)
	respData, err = client.Get(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	})

	log.Println("打印测试数据----查询退款订单----- GetRefundsInfo", respData)
	return
}

func ApiGetWeChatPayOrder(outOrderNo string, wc *paysetting.PaySetting) (respData *PayData, err error) {
	param := fmt.Sprintf("/id/%s?mchid=%s",
		outOrderNo, wc.MchId)
	url := StrJoin(wxQueryPayOrders, param)
	authorization, err := GenerateToken("GET", url, "", wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}

	url = StrJoin(wxBaseUrl, url)
	//log.Println("打印测试数据 ApiQueryPayScoreOrder-url", url)

	respData, err = client.Get(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"User-Agent":    userAgent,
		"Authorization": authorization,
	})
	log.Println("打印测试数据----查询微信支付订单----- ApiQueryPayScoreOrder-respData", respData)

	return
}

//查询投诉单列表 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_11.shtml
func ApiGetComplaintsList(urlData string, wc *paysetting.PaySetting) (respData map[string]interface{}, err error) {
	url := StrJoin(complaintsList, urlData)
	authorization, err := GenerateToken("GET", url, "", wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}
	url = StrJoin(wxBaseUrl, url)
	respData, err = authorized.GetRequest(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	})

	log.Println("打印测试数据----查询投诉单列表----- ApiGetComplaintsList", respData)
	return respData, nil
}

// 查询投诉单详情 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_13.shtml
func ApiGetComplaintsDetails(complaintId string, wc *paysetting.PaySetting) (respData map[string]interface{}, err error) {
	url := StrJoin(complaintsDetails, "/", complaintId)
	authorization, err := GenerateToken("GET", url, "", wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}
	url = StrJoin(wxBaseUrl, url)
	respData, err = authorized.GetRequest(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	})

	log.Println("打印测试数据----查询投诉单详情----- ApiGetComplaintsList", respData)
	return respData, nil
}

// 查询投诉协商历史 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_12.shtml
func ApiGetComplaintsNegotiationHistory(urlData string, wc *paysetting.PaySetting) (respData map[string]interface{}, err error) {
	url := StrJoin(complaintsDetails, urlData)
	authorization, err := GenerateToken("GET", url, "", wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}
	url = StrJoin(wxBaseUrl, url)
	respData, err = authorized.GetRequest(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	})

	log.Println("打印测试数据----查询投诉协商历史----- ApiGetComplaintsList", respData)
	return respData, nil
}

// 创建投诉通知回调地址 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_2.shtml
func ApiCreateComplaintsNotifyLogicURL(notifyLogicURL string, wc *paysetting.PaySetting) (map[string]interface{}, error) {

	param := struct {
		URL string `json:"url"`
	}{}
	param.URL = notifyLogicURL
	payload, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	url := createNotifyLogicURL
	authorization, err := GenerateToken("POST", url, string(payload), wc)
	if err != nil {
		return nil, err
	}
	url = StrJoin(wxBaseUrl, url)

	respMap, err := authorized.PostRequest(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, bytes.NewReader(payload))
	if err != nil {
		return nil, err
	}
	return respMap, nil
}

// 查询投诉通知回调地址 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_3.shtml
func ApiGetComplaintsNotifyLogicURL(wc *paysetting.PaySetting) (map[string]interface{}, error) {
	authorization, err := GenerateToken("GET", createNotifyLogicURL, "", wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}
	url := StrJoin(wxBaseUrl, createNotifyLogicURL)
	respData, err := authorized.GetRequest(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	})

	log.Println("打印测试数据----查询投诉通知回调地址----- ApiGetComplaintsList", respData)
	return respData, nil
}

// 更新投诉通知回调地址 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_4.shtml
func ApiPutComplaintsNotifyLogicURL(notifyLogicURL string, wc *paysetting.PaySetting) (map[string]interface{}, error) {
	param := struct {
		URL string `json:"url"`
	}{}
	param.URL = notifyLogicURL
	payload, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	url := createNotifyLogicURL
	authorization, err := GenerateToken("PUT", url, string(payload), wc)
	if err != nil {
		return nil, err
	}
	url = StrJoin(wxBaseUrl, url)

	respMap, err := authorized.RestRequest("PUT", url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, bytes.NewReader(payload))
	if err != nil {
		return nil, err
	}
	return respMap, nil
}

// 删除投诉通知回调地址 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_5.shtml
func ApiDeleteComplaintsNotifyLogicURL(wc *paysetting.PaySetting) (map[string]interface{}, error) {
	authorization, err := GenerateToken("DELETE", createNotifyLogicURL, "", wc)
	if err != nil {
		return nil, err
	}

	//client := clientPool.Get().(*PayClient)
	//defer clientPool.Put(client)
	var client *PayClient
	if wc.TypeName == "zm" {
		client = zmClientPool.Get().(*PayClient)
		defer zmClientPool.Put(client)
	} else {
		client = clientPool.Get().(*PayClient)
		defer clientPool.Put(client)
	}
	url := StrJoin(wxBaseUrl, createNotifyLogicURL)
	respData, err := authorized.RestRequest("DELETE", url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, nil)

	log.Println("打印测试数据----查询投诉通知回调地址----- ApiGetComplaintsList", respData)
	return respData, nil
}

// 提交回复 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_14.shtml
func ApiSubmitReplyComplaints(param *request.ComplaintsSubmitReq, wc *paysetting.PaySetting) error {
	//param := struct {
	//	ComplaintedMchid string   `json:"complainted_mchid"`
	//	ResponseContent  string   `json:"response_content"`
	//	ResponseImages   []string `json:"response_images"`
	//}{}
	//
	//param.ComplaintedMchid = mapData["complainted_mchid"].(string)
	//param.ResponseContent = mapData["response_content"].(string)
	//param.ResponseImages = mapData["response_images"].([]string)
	param.ComplaintMchId = wc.MchId

	payload, err := json.Marshal(param)
	if err != nil {
		return err
	}
	url := complaintsDetails + "/" + param.ComplaintId + "/response"
	authorization, err := GenerateToken("POST", url, string(payload), wc)
	if err != nil {
		return err
	}
	url = StrJoin(wxBaseUrl, url)
	respMap, err := authorized.PostRequest(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, bytes.NewReader(payload))
	if err != nil {
		return err
	}

	fmt.Println(respMap)
	return nil
}

//反馈处理完成 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_15.shtml
func ApiFeedbackSolveComplaints(complaintId string, wc *paysetting.PaySetting) error {
	param := struct {
		ComplaintedMchid string `json:"complainted_mchid"`
	}{}
	param.ComplaintedMchid = wc.MchId
	payload, err := json.Marshal(param)
	if err != nil {
		return err
	}

	url := complaintsDetails + "/" + complaintId + "/complete"
	authorization, err := GenerateToken("POST", url, string(payload), wc)
	if err != nil {
		return err
	}
	url = StrJoin(wxBaseUrl, url)

	respMap, err := authorized.PostRequest(url, map[string]string{
		"Content-Type":  "application/json; charset=utf-8",
		"Accept":        "application/json",
		"Authorization": authorization,
	}, bytes.NewReader(payload))
	if err != nil {
		return err
	}
	fmt.Println(respMap)
	return nil
}

// 商户上传反馈图片 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_10.shtml
func ApiMediaUpload(ctx context.Context, req io.Reader, wc *paysetting.PaySetting) (resp map[string]interface{}, err error) {
	// 图片大小不能超过2M，只支持JPG、BMP、PNG格式,
	fBytes, err := ioutil.ReadAll(req)
	if err != nil {
		return
	}
	if len(fBytes) > 2*1024*1024 {
		err = errors.New("图片大小不能超过2M")
		return
	}
	// io reader cannot read multiple times
	_, format, err := image.DecodeConfig(bytes.NewReader(fBytes))
	if err != nil {
		return
	}
	var ctype string
	var suffix = "jpg"
	switch format {
	case "jpeg", "jpg":
		ctype = "image/jpg"
		suffix = "jpg"
	case "bmp":
		ctype = "image/bmp"
		suffix = "bmp"
	case "png":
		ctype = "image/png"
		suffix = "png"
	default:
		err = fmt.Errorf("不支持的图片格式:%s", format)
		return
	}
	res, err := doFormUpload(ctx, uploadImage, fBytes, "image."+suffix, ctype, wc)
	if err != nil {
		return
	}
	err = json.Unmarshal(res, &resp)
	return
}

//微信临时素材上传
func doFormUpload(ctx context.Context, url string, fBytes []byte, fName string, fileType string, wc *paysetting.PaySetting) (resp []byte, err error) {
	nonce := authorized.GetRandomString(32)
	timestamp := int(time.Now().Unix())

	hash := sha256.Sum256(fBytes)
	meta := struct {
		Filename string `json:"filename"`
		Sha256   string `json:"sha256"`
	}{
		Filename: fName,
		Sha256:   hex.EncodeToString(hash[:]),
	}
	metaStr, _ := json.Marshal(meta)
	apiClientKeyFile, err := os.Open(wc.ApiclientKeyPath)
	apiClientBytes, err := ioutil.ReadAll(apiClientKeyFile)
	apiClientKey := string(apiClientBytes)
	apiPriKey, err := buildRSAPrivateKey(apiClientKey)
	boundary := "boundary"
	signature, _ := CreateSignature("POST", url, timestamp, nonce, metaStr, apiPriKey)

	auth := fmt.Sprintf("%s mchid=\"%s\",nonce_str=\"%s\",serial_no=\"%s\",timestamp=\"%d\",signature=\"%s\"", "WECHATPAY2-SHA256-RSA2048", wc.MchId, nonce, wc.SerialNo, timestamp, signature)

	reqBody := fmt.Sprintf("--%s\r\nContent-Disposition: form-data; name=\"meta\";\r\nContent-Type: application/json\r\n\r\n%s\r\n--%s\r\nContent-Disposition: form-data; name=\"file\"; filename=\"%s\";\r\nContent-Type: %s\r\n\r\n%s\r\n--%s--", boundary, metaStr, boundary, fName, fileType, fBytes, boundary)
	h := &http.Client{}
	requestUrl := wxBaseUrl + url
	req, _ := http.NewRequestWithContext(ctx, "POST", requestUrl, strings.NewReader(reqBody))
	req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data;boundary=%s", boundary))
	req.Header.Set("Authorization", auth)
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", "Mozilla/5.0")
	rawResp, err := h.Do(req)
	if err != nil {
		return
	}
	resp, err = ioutil.ReadAll(rawResp.Body)
	if err != nil {
		return
	}
	// 验证resp签名
	//weChatSignature := rawResp.Header.Get("Wechatpay-Signature")
	//weChatNonce := rawResp.Header.Get("Wechatpay-Nonce")
	//timestampStr := rawResp.Header.Get("Wechatpay-Timestamp")
	//weChatSerial := rawResp.Header.Get("Wechatpay-Serial")
	//var platformCertMap PlatformCertificatesMap
	//
	//pub := platformCertMap.GetPublicKey(weChatSerial)
	//fmt.Println(pub)
	//if !VerifyWeChatSignature(timestampStr, weChatNonce, resp, weChatSignature, pub) {
	//	err = errors.New("resp签名错误")
	//	return
	//}
	return
}

type PlatformCertificatesMap interface {
	GetPublicKey(serialNo string) (pubKey *rsa.PublicKey)
}

// 生成API调用时需要的签名
func CreateSignature(method string, url string, ts int, nounce string, body []byte, priKey *rsa.PrivateKey) (signature string, err error) {
	// 签名前的字符串
	sBeforeSign := strings.Join([]string{method, url, fmt.Sprintf("%d", ts), nounce}, "\n") + "\n"
	if method == "GET" {
		sBeforeSign += "\n"
	} else {
		sBeforeSign += string(body) + "\n"
	}

	signature, err = sha256WithRSA(sBeforeSign, priKey)
	return
}

func sha256WithRSA(sBeforeSign string, priKey *rsa.PrivateKey) (signature string, err error) {
	h := sha256.New()
	_, _ = h.Write([]byte(sBeforeSign))
	hashed := h.Sum(nil)

	sign, err := rsa.SignPKCS1v15(rand.Reader, priKey, crypto.SHA256, hashed)
	if err != nil {
		return
	}
	signature = base64.StdEncoding.EncodeToString(sign)
	return
}

// 生成rsa私钥
func buildRSAPrivateKey(keyContent string) (priKey *rsa.PrivateKey, err error) {
	block, _ := pem.Decode([]byte(keyContent))
	if block == nil {
		err = errors.New("private key error")
		return
	}
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return
	}
	priKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		err = errors.New("private key 不是rsa格式")
		return
	}
	return
}

// 验证API返回和回调header中的微信签名
func VerifyWeChatSignature(ts string, nonce string, body []byte, b64Sig string, pub *rsa.PublicKey) (pass bool) {
	// 签名前的字符串
	sBeforeSign := strings.Join([]string{ts, nonce, string(body)}, "\n") + "\n"
	signature, err := base64.StdEncoding.DecodeString(b64Sig)
	if err != nil {
		pass = false
		return
	}
	// 对签名字符串进行hash
	h := sha256.New()
	_, _ = h.Write([]byte(sBeforeSign))
	hashed := h.Sum(nil)
	verifyErr := rsa.VerifyPKCS1v15(pub, crypto.SHA256, hashed, signature)
	if verifyErr != nil {
		pass = false
		return
	}
	pass = true
	return
}

func DecryptOAEP(cipherText, privateKey string) string {
	rsaPrivateKey := ParsePKCS1PrivateKey(privateKey)
	cipherData, _ := base64.StdEncoding.DecodeString(cipherText)
	rng := rand.Reader
	plaintext, err := rsa.DecryptOAEP(sha1.New(), rng, rsaPrivateKey, cipherData, nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from decryption: %s\n", err)
		return ""
	}
	return string(plaintext)
}

func ParsePKCS1PrivateKey(privateKeyPath string) *rsa.PrivateKey {
	privateKey, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	block, _ := pem.Decode(privateKey)
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	priKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		err = errors.New("private key 不是rsa格式")
		return nil
	}
	return priKey
}
